package jwt_auth

import (
	"errors"
	"gitee.com/dennis-kk/service-box-go/util/http_proxy"
	"github.com/golang-jwt/jwt/v4"
	"strings"
)

var (
	ErrInvalidTokenType = errors.New("invalid token type")
	ErrInvalidToken     = errors.New("invalid token string")
	ErrInvalidPayload   = errors.New("invalid payload parameter")
)

type (
	//Payload 自定义payload结构，支持从这个接口序列化和反序列化
	Payload interface {
		Valid() error
	}

	//JwtAuth jwt鉴权模块，包装常用的jwt工具函数
	JwtAuth struct {
		//opt Jwt 配置
		opt *Options
	}
)

// GetTokenString 鉴权函数，提取token 字符串
// param c http 请求上下文
// return token 完整字符串
// return error 解析是否发生错误，失败会返回ErrInvalidTokenType
func (a *JwtAuth) GetTokenString(c *http_proxy.Context) (string, error) {
	// 获取http消息中token头
	authHeader := c.GetHeader(a.opt.AuthHeader)
	if !strings.HasPrefix(authHeader, a.opt.TokenType+" ") {
		return "", ErrInvalidTokenType
	}

	//token 字符串
	return authHeader[len(a.opt.TokenType)+1:], nil
}

// GetToken 解析并获取token结构体
// param token token 字符串
// return payload map
func (a *JwtAuth) GetToken(token string) (map[string]interface{}, error) {
	t, err := jwt.Parse(token, a.secretKey)
	if err != nil {
		return nil, err
	}

	claims, ok := t.Claims.(jwt.MapClaims)
	if !ok {
		return nil, ErrInvalidToken
	}
	return claims, nil
}

// GetPayloadFromToken 解析token，并尝试读取payload信息
// param token token 字符串
// payload 用户自定义payload结构体指针
// return 解析成功返回nil， 否则返回错误
func (a *JwtAuth) GetPayloadFromToken(token string, payload Payload) error {
	_, err := jwt.ParseWithClaims(token, payload, a.secretKey)
	if err != nil {
		return err
	}
	return nil
}

// GenTokenWithPayload 生成jwt token 字符串
// param payload 用户自定义data, 能序列化为json
// return jwt 字符串
func (a *JwtAuth) GenTokenWithPayload(payload Payload) (string, error) {
	if payload == nil {
		return "", ErrInvalidPayload
	}

	token := jwt.New(jwt.GetSigningMethod(a.opt.Encryption))
	token.Claims = payload

	tokenStr, err := token.SignedString([]byte(a.opt.Secret))
	if err != nil {
		return "", err
	}

	return tokenStr, nil
}

// GenTokenWithClaims 使用用户自定义map生成 jwt token
// param payload 用户自定义data map
// return jwt 字符串
func (a *JwtAuth) GenTokenWithClaims(payload map[string]interface{}) (string, error) {

	if payload == nil {
		return "", ErrInvalidPayload
	}

	token := jwt.New(jwt.GetSigningMethod(a.opt.Encryption))
	claims := token.Claims.(jwt.MapClaims)
	for k := range payload {
		claims[k] = payload[k]
	}

	tokenStr, err := token.SignedString([]byte(a.opt.Secret))
	if err != nil {
		return "", err
	}

	return tokenStr, nil
}

// ============== helper ==============
func (a JwtAuth) secretKey(_ *jwt.Token) (interface{}, error) {
	return []byte(a.opt.Secret), nil
}
